/*
  Small Game Library  SDLVERSION
  v 0.1
*/

#include "sgl.h"
// INTERNAL
//====================================================================================================


Uint32 ticks;
sgl_surface *SGLscreen;
Uint32 colorkey;
Mix_Music *music;


char *sgl_fontPath;

// check if a file exists
int fileExists( char *name ){
    FILE    *handle;
    // try to open the file
    handle = fopen( name, "rb" );
    if (handle == NULL) return 0;
    fclose( handle );
    return -1;
}

// allocate and fill a string
char *let(char *string){
     char *buffer;
     int l;
     l=strlen(string);
     buffer =(char *) malloc(l);
     strcpy(buffer,string);
     return string;
}
//====================================================================================================


Uint8 *sgl_keystate;
Uint8 lastkey;

int sgl_xmouse;
int sgl_ymouse;
int sgl_bmouse;

int sgl_volume;


// START && SURFACE
//====================================================================================================

//start sgl initialize sdl and open the screen | return the screen surface
sgl_surface *sgl_start( int w, int h, char *title){
    colorkey=0;
    SGLscreen = NULL;

    // Initialize SDL
    if (SDL_Init (SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK) < 0) {
	fprintf (stderr, "Couldn't initialize SDL: %s\n", SDL_GetError ());
	exit (1);
    }
    atexit (sgl_terminate);
    
    Mix_OpenAudio( 44100, AUDIO_S16SYS, 2, 8192) ;
    TTF_Init();

    // Set 32-bits video mode
    SGLscreen = SDL_SetVideoMode (w, h, 0, SDL_HWSURFACE |SDL_DOUBLEBUF );
    if (SGLscreen == NULL) {
	fprintf (stderr, "Couldn't set video mode: %s\n", SDL_GetError ());
	exit (2);
    }
  //Set caption
    SDL_WM_SetCaption( title, NULL );

    sgl_volume=0;
    return SGLscreen;
}
//____________________________________________________________________________________________________

//stop sdl and terminate the program
void sgl_terminate(){
        TTF_Quit();
	Mix_CloseAudio();
	SDL_Quit();
}
//____________________________________________________________________________________________________

//simulate the page swapping and update the events
void sgl_screenswap(){
    SDL_Event event;
    int i;
    while(ticks>SDL_GetTicks());

    while (SDL_PollEvent (&event)){
	switch (event.type){
	    case SDL_KEYDOWN:
		// if press Ctrl + C, terminate program
		if ( (event.key.keysym.sym == SDLK_c) && (event.key.keysym.mod & (KMOD_LCTRL | KMOD_RCTRL)) )exit(2);
	    
		lastkey = event.key.keysym.unicode;
		if (lastkey==0){
			switch(event.key.keysym.sym){
				case 127:
					lastkey=127;
					break;
				case 256:
					lastkey=48;
					break;
				case 257:
					lastkey=49;
					break;
				case 258:
					lastkey=50;
					break;
				case 259:
					lastkey=51;
					break;
				case 260:
					lastkey=52;
					break;
				case 261:
					lastkey=53;
					break;
				case 262:
					lastkey=54;
					break;
				case 263:
					lastkey=55;
					break;
				case 264:
					lastkey=56;
					break;
				case 265:
					lastkey=57;
					break;
				case 266:
					lastkey=46;
					break;
				case 267:
					lastkey=47;
					break;
				case 271:
					lastkey=13;
					break;				
				default:
					lastkey=0;
					break;

				}
			}
		break;

	    case SDL_KEYUP:
		lastkey=0;  
		sgl_keystate = SDL_GetKeyState(NULL);
		break;

	    case SDL_QUIT:
		sgl_terminate();   
		exit(2);
		break;

	    default:
		break;

	}
    }
	sgl_keystate = SDL_GetKeyState(NULL);
	sgl_bmouse=SDL_GetMouseState( &sgl_xmouse, &sgl_ymouse);

    SDL_Flip(SGLscreen);

    ticks=SDL_GetTicks()+2;//sincronizations
}
//____________________________________________________________________________________________________


//set the color of trasparency
void sgl_set_colorkey(Uint32 color){
    colorkey=color;
}
//____________________________________________________________________________________________________

//create an empty surface
sgl_surface *sgl_surface_create(int w, int h, int option){
    sgl_surface *surface;
    switch(option){
	case SGL_COLORKEY:
	    surface= SDL_CreateRGBSurface(SDL_HWSURFACE, w, h,32,0,0,0,0);
	    SDL_SetColorKey(surface, (SDL_SRCCOLORKEY),colorkey);
	    break;
	case SGL_ALPHA:
	    surface= SDL_CreateRGBSurface(SDL_HWSURFACE, w, h,32,0,0,0,0);
	    SDL_SetAlpha (surface, SDL_SRCALPHA|SDL_RLEACCEL, 0) ;
	    break;
	default:
	    surface= SDL_CreateRGBSurface(SDL_HWSURFACE, w, h,32,0,0,0,0);
	    break;
    }
    return surface;
}
//____________________________________________________________________________________________________

//load an image like surface
sgl_surface *sgl_surface_load(char *filename,int option){
    sgl_surface *surface=NULL;
    switch(option){
	case SGL_COLORKEY:
	    if( fileExists(filename)){
		surface =SDL_DisplayFormat(IMG_Load(filename));
		SDL_SetColorKey(surface, (SDL_SRCCOLORKEY),(Uint32)colorkey);
	    }
	    break;
	case SGL_ALPHA:
	    if( fileExists(filename)){
		surface =SDL_DisplayFormatAlpha(IMG_Load(filename));
	    }
	    break;
	default:
	    if( fileExists(filename)){
		surface =SDL_DisplayFormat(IMG_Load(filename));
	    }
	    break;
    }
    return surface;
}
//____________________________________________________________________________________________________

//erase a surface
void sgl_surface_destroy( sgl_surface *surface){
    if (surface!=NULL)
	SDL_FreeSurface(surface);
}
//____________________________________________________________________________________________________


//blit a surface in another or in a screen
void sgl_paste( sgl_surface *dst, int x, int y, sgl_surface *src){
	SDL_Rect d;
    if (dst!=NULL &&src!=NULL){
		d.x=x;
		d.y=y;
        d.w=src->w;
        d.h=src->h;
        SDL_BlitSurface(src, NULL, dst, &d );
    }
}
//____________________________________________________________________________________________________

//blit a part of surface in another
void sgl_blt( sgl_surface *dst, int x, int y, sgl_surface *src, int xs, int ys, int w, int h){
	SDL_Rect s;
	SDL_Rect d;

    if (dst!=NULL &&src!=NULL){
		s.x=xs;
		s.y=ys;
        s.w=w;
        s.h=h;

		d.x=x;
		d.y=y;
        d.w=w;
        d.h=h;

        SDL_BlitSurface(src, &s, dst, &d );
    }
}
//____________________________________________________________________________________________________

// pixel precision surfaces collisions
int sgl_hit(sgl_surface *surfacea, int xa, int ya, sgl_surface *surfaceb, int xb, int yb){
    int xa2,ya2,xb2,yb2;
    int xaa,yaa,xbb,ybb;
    int xw,yh;
    int x,y;
    int cc;

    xa2=xa+surfacea->w;
    ya2=ya+surfacea->h;
    xb2=xb+surfaceb->w;
    yb2=yb+surfaceb->h;



    xaa=0;
    xbb=0;
    if (xa<xb)
	   xaa=MAX(xa,xb)-MIN(xa,xb);
    else
	    xbb=MAX(xa,xb)-MIN(xa,xb);

    yaa=0;
    ybb=0;
   if (ya<yb)
	yaa=MAX(ya,yb)-MIN(ya,yb);
    else
	ybb=MAX(ya,yb)-MIN(ya,yb);

    xw=MIN(xa2,xb2)-MAX(xa,xb);
    yh=MIN(ya2,yb2)-MAX(ya,yb);

    for (y=0; y<yh; y++){
	    for (x=0; x<xw; x++){
	        if(sgl_point(surfacea,xaa+x,yaa+y)!=colorkey){
		    if(sgl_point(surfaceb,xbb+x,ybb+y)!=colorkey){
			return 1;
		    }
		}
	    }
    }

    return 0;
}

//____________________________________________________________________________________________________

// GRAPHICS PRIMITIVES
//====================================================================================================

//return a color from rgb
Uint32 sgl_rgb( int r, int g, int b){
	return b+(g*256)+(r*256*256);
}
//____________________________________________________________________________________________________

//clear all screen (needed after screen swap)
void sgl_cls( sgl_surface *dst,Uint32 color){
         SDL_FillRect (dst, NULL, color);
     }
//____________________________________________________________________________________________________

//plot a pixel in surface
void sgl_plot( sgl_surface *surface, int x, int y, Uint32 color ){
/* 
	SDL_Rect d;
     d.x=x;
	 d.y=y;
	 d.w=1;
	 d.h=1;
     SDL_FillRect (surface, &d, color);
*/
   Uint8 *p;
	if (x>=0 && y>=0 && x<surface->w && y<surface->h){
		p=(Uint8 *)surface->pixels + (y * surface->pitch) + x * 4;
		*(Uint32 *)p=color;
	}
}
//____________________________________________________________________________________________________

//return the color of pixel in surface 0 if out of surface
Uint32 sgl_point( sgl_surface *surface, int x, int y){
    Uint8 *p;
    if (x>=0 && y>=0 && x<surface->w && y<surface->h){
	p=(Uint8 *)surface->pixels + y * surface->pitch + x * 4;
	return *(Uint32 *)p;
    Uint8 *p;
    }
    else{
	    return 0;
    }
}
//____________________________________________________________________________________________________

// draw a horizontal line
void sgl_hline( sgl_surface * surface, int x1, int y1, int w, Uint32 color){
     int x;
     for(x=0;x<w;x++)sgl_plot( surface,x1+x,y1,color);
}
//____________________________________________________________________________________________________

// draw a vertical line
void sgl_vline( sgl_surface * surface, int x1, int y1, int h, Uint32 color){
     int y;
     for(y=0;y<h;y++)sgl_plot( surface,x1,y1+y,color);
}
//____________________________________________________________________________________________________

//  draw a line
void sgl_line( sgl_surface * surface, int x1, int y1, int x2, int y2, Uint32 color){
    //  Draw a line WITHOUT REFRESH VIDEO (to use with other function like rect)
    static int c,x,y;
    double slope;

    int dx = x2 - x1;  // change in x
    int dy = y2 - y1;  // change in y

    slope = (double)dy / dx;

    if (abs(dx) >= abs(dy)) {
        // draw left/right - right/left
        c = (x1 <= x2) ? 1 : -1;
        for (x = x1; x != x2 + c; x += c) {
            y = y1 + (int)( (x - x1) * slope + 0.5 );
            sgl_plot(surface, x, y, color);
        }
    }
    else
    {
        // draw top/bottom - bottom/top
        c = (y1 <= y2) ? 1 : -1;
        for (y = y1; y != y2 + c; y += c) {
            x = x1 + (int)( (y - y1) / slope + 0.5 );
            sgl_plot(surface, x, y, color);
        }
    }
}
//____________________________________________________________________________________________________

// draw empty rect
void sgl_box( sgl_surface *surface, int x, int y, int w, int h, Uint32 color){
     int i;
     for(i=0;i<=w;i++){sgl_plot(surface,x+i,y,color);sgl_plot(surface,x+i,y+h,color);}
     for(i=0;i<=h;i++){sgl_plot(surface,x,y+i,color);sgl_plot(surface,x+w,y+i,color);}
}
//____________________________________________________________________________________________________

// draw fill rect
void sgl_bar( sgl_surface *surface, int x, int y, int w, int h, Uint32 color){
     SDL_Rect d;
     d.x=x;
	 d.y=y;
	 d.w=w;
	 d.h=h;
     SDL_FillRect (surface, &d, color);
}
//____________________________________________________________________________________________________

// draw empty circle
void sgl_circle( sgl_surface *surface, int vx, int vy, int r, Uint32 color){

     Sint16 x = 0;
     Sint16 y = r-1;     //radius zero == draw nothing
     Sint16 d = 3 - 2*r;
     Sint16 diagonalInc = 10 - 4*r;
     Sint16 rightInc = 6;

     while (x <= y) {
         sgl_plot(surface,vx+x,vy+y,color);
         sgl_plot(surface,vx+x,vy-y,color);
         sgl_plot(surface,vx-x,vy+y,color);
         sgl_plot(surface,vx-x,vy-y,color);

         sgl_plot(surface,vx+y,vy+x,color);
         sgl_plot(surface,vx+y,vy-x,color);
         sgl_plot(surface,vx-y,vy+x,color);
         sgl_plot(surface,vx-y,vy-x,color);


         if (d >=  0) {
            d += diagonalInc;
            diagonalInc += 8;
            y -= 1;
         }
         else {
              d += rightInc;
              diagonalInc += 4;
         }
         rightInc += 4;
         x += 1;
     }
}
//____________________________________________________________________________________________________

// draw fill circle
void sgl_fillcircle( sgl_surface *surface, int vx, int vy, int r, Uint32 color){
  Sint16 x = 0;
  Sint16 y = r-1;     //radius zero == draw nothing
  Sint16 d = 3 - 2*r;
  Sint16 diagonalInc = 10 - 4*r;
  Sint16 rightInc = 6;

  while (x <= y) {
    sgl_hline(surface,vx-x,vy+y,x+x,color);
    sgl_hline(surface,vx-x,vy-y,x+x,color);
    sgl_hline(surface,vx-y,vy+x,y+y,color);
    sgl_hline(surface,vx-y,vy-x,y+y,color);
    if (d >=  0) {
          d += diagonalInc;
          diagonalInc += 8;
          y -= 1;
    }
    else {
         d += rightInc;
         diagonalInc += 4;
    }
    rightInc += 4;
    x += 1;
  }
}
//____________________________________________________________________________________________________

// draw empty ellipse
void sgl_ellipse( sgl_surface *surface, int vx, int vy, int Xradius, int Yradius, Uint32 color){
  Sint32 x, y;
  Sint32 Xchange, Ychange;
  Sint32 EllipseError;
  Sint32 TwoASquare, TwoBSquare;
  Sint32 StoppingX, StoppingY;

  TwoASquare = 2*Xradius*Xradius;
  TwoBSquare = 2*Yradius*Yradius;

  // 1st set of points
  x = Xradius-1;  // radius zero == draw nothing
  y = 0;

  Xchange = Yradius*Yradius*(1-2*Xradius);
  Ychange = Xradius*Xradius;

  EllipseError = 0;

  StoppingX = TwoBSquare*Xradius;
  StoppingY = 0;

  // Plot four ellipse points by iteration
  while (StoppingX > StoppingY) {

    sgl_plot(surface,vx+x,vy+y,color);
    sgl_plot(surface,vx+x,vy-y,color);
    sgl_plot(surface,vx-x,vy+y,color);
    sgl_plot(surface,vx-x,vy-y,color);


    ++y;
    StoppingY    += TwoASquare;
    EllipseError += Ychange;
    Ychange      += TwoASquare;
    if (( 2*EllipseError + Xchange) > 0) {
      --x;
      StoppingX    -= TwoBSquare;
      EllipseError += Xchange;
      Xchange      += TwoBSquare;
    }
  }// while

  // 2nd set of points
  x = 0;
  y = Yradius-1;  // radius zero == draw nothing
  Xchange = Yradius*Yradius;
  Ychange = Xradius*Xradius*(1-2*Yradius);
  EllipseError = 0;
  StoppingX = 0;
  StoppingY = TwoASquare*Yradius;

  //Plot four ellipse points by iteration
  while (StoppingX < StoppingY) {

    sgl_plot(surface,vx+x,vy+y,color);
    sgl_plot(surface,vx+x,vy-y,color);
    sgl_plot(surface,vx-x,vy+y,color);
    sgl_plot(surface,vx-x,vy-y,color);

    ++x;
    StoppingX    += TwoBSquare;
    EllipseError += Xchange;
    Xchange      += TwoBSquare;
    if ((2*EllipseError + Ychange) > 0) {
      --y;
      StoppingY    -= TwoASquare;
      EllipseError += Ychange;
      Ychange      += TwoASquare;
    }
  }
}
//____________________________________________________________________________________________________

// draw fill ellipse
void sgl_fillellipse( sgl_surface *surface, int vx, int vy, int Xradius, int Yradius, Uint32 color){
  Sint32 x, y;
  Sint32 Xchange, Ychange;
  Sint32 EllipseError;
  Sint32 TwoASquare, TwoBSquare;
  Sint32 StoppingX, StoppingY;

  TwoASquare = 2*Xradius*Xradius;
  TwoBSquare = 2*Yradius*Yradius;

  //1st set of points
  x = Xradius-1;  //radius zero == draw nothing
  y = 0;

  Xchange = Yradius*Yradius*(1-2*Xradius);
  Ychange = Xradius*Xradius;

  EllipseError = 0;

  StoppingX = TwoBSquare*Xradius;
  StoppingY = 0;

  //Plot four ellipse points by iteration
  while (StoppingX > StoppingY) {

    sgl_hline(surface,vx-x,vy+y,x*2,color);
    sgl_hline(surface,vx-x,vy-y,x*2,color);


    ++y;
    StoppingY    += TwoASquare;
    EllipseError += Ychange;
    Ychange      += TwoASquare;
    if (( 2*EllipseError + Xchange) > 0) {
      --x;
      StoppingX    -= TwoBSquare;
      EllipseError += Xchange;
      Xchange      += TwoBSquare;
    }
  }//while

  //2nd set of points
  x = 0;
  y = Yradius-1;  //radius zero == draw nothing
  Xchange = Yradius*Yradius;
  Ychange = Xradius*Xradius*(1-2*Yradius);
  EllipseError = 0;
  StoppingX = 0;
  StoppingY = TwoASquare*Yradius;

  //Plot four ellipse points by iteration
  while (StoppingX < StoppingY) {

    sgl_hline(surface,vx-x,vy+y,x*2,color);
    sgl_hline(surface,vx-x,vy-y,x*2,color);

    ++x;
    StoppingX    += TwoBSquare;
    EllipseError += Xchange;
    Xchange      += TwoBSquare;
    if ((2*EllipseError + Ychange) > 0) {
      --y;
      StoppingY    -= TwoASquare;
      EllipseError += Ychange;
      Ychange      += TwoASquare;
    }
  }
}
//____________________________________________________________________________________________________

// paint close area
void sgl_paint(sgl_surface *surface, int vx, int vy, Uint32 color){
	char *paintmap;
	int xstart,ystart;
	int src_col,col;
	char pm;

	paintmap= malloc(surface->w * surface->h);

	xstart=vx;
	ystart=vy;
	src_col=sgl_point(surface,vx,vy);
	sgl_plot(surface,vx,vy,color);
	col=sgl_point(surface,vx,vy);
    pm=2;
    if (col==src_col)return;

    while(0==0) {
	if (sgl_point(surface,vx,vy)==src_col) {
		sgl_plot(surface,vx,vy,color);
		paintmap[vy*surface->w+vx]=pm;
    	}

	if (sgl_point(surface,vx+1,vy)==src_col && (vx+1) < surface->w) {
		    vx=vx+1;
		    pm=1;
		}
	else if (sgl_point(surface,vx-1,vy)==src_col && (vx-1) > 0 ) {
		    vx=vx-1;
		    pm=2;
		}
	else if (sgl_point(surface,vx,vy+1)==src_col && (vy+1) < surface->h) {
		    vy=vy+1;
		    pm=3;
		}
	else if (sgl_point(surface, vx, vy-1)==src_col && (vy-1) > 0) {
		    vy=vy-1;
		    pm=4;
		}
	else{
		if (vx==xstart && vy==ystart)break;

		if (paintmap[vy*surface->w+vx]==1)
			vx=vx-1;
		else if (paintmap[vy*surface->w+vx]==2)
			vx=vx+1;
		else if (paintmap[vy*surface->w+vx]==3)
			vy=vy-1;
		else if (paintmap[vy*surface->w+vx]==4)
			vy=vy+1;
		}
	}
	free(paintmap);
}


//____________________________________________________________________________________________________

//sounds & music
//====================================================================================================

//load a sound
sgl_sound *sgl_sound_load( char *filename){
     if (fileExists(filename))
          return Mix_LoadWAV(filename);
     else
          return NULL;
}
//____________________________________________________________________________________________________

//play a sound
void sgl_sound_play( sgl_sound  *snd, int channel, int volume, int loop){
         Mix_Volume(channel,MID(volume+sgl_volume,0,127));
         Mix_PlayChannel( channel, snd, loop);
}
//____________________________________________________________________________________________________

//remove a sound loaded
void sgl_sound_destroy( sgl_sound  *snd){
     if (snd!=NULL)
  		 Mix_FreeChunk(snd);
}
//____________________________________________________________________________________________________

//load a music
void sgl_music_load( char *filename){
     if (fileExists(filename))
          music = Mix_LoadMUS(filename);
     else
          music= NULL;
}
//____________________________________________________________________________________________________

//play a music | loop=0 stop 1=play 2=looping
void sgl_music_play( int volume, int loop){
     Mix_VolumeMusic(MID(volume+sgl_volume,0,127));
     switch(loop){
           case 0:
               	Mix_HaltMusic();
                break;
           case 1:
     		    Mix_PlayMusic( music, 1);
                break;

           case -1:
     		    Mix_PlayMusic( music, -1);
                break;
     }
}
//____________________________________________________________________________________________________

//change the current music volume
int sgl_music_volume( int volume){
    return Mix_VolumeMusic(MID(volume+sgl_volume,0,127));
}
//____________________________________________________________________________________________________

//text
//====================================================================================================

// set the current fontpath
void sgl_setfont( char *fontPath){
     sgl_fontPath=let(fontPath);
}
//____________________________________________________________________________________________________

//make a text as surface
sgl_surface *sgl_text(  char *string, int size, Uint32 col){
        sgl_surface *txt;
        TTF_Font *font;
        SDL_Color Col;

        Col.b=(col & 255) ;
        Col.g=((col/256) & 255);
        Col.r=((col/(256*256)) & 255);

        font=TTF_OpenFont( sgl_fontPath, size);
        if (font!=NULL){
           txt= TTF_RenderText_Blended( font, string, Col);
       	   TTF_CloseFont(font);
           return txt;
        }
        else{
             return NULL;
        }
}
//____________________________________________________________________________________________________

//print a text in a surface
void sgl_print_text (sgl_surface *dst, int x, int y, int size, char *string, Uint32 col) {
	sgl_surface *tmp;
	
	tmp=sgl_text( string, size, col);
	sgl_paste(dst, x, y, tmp);
	sgl_surface_destroy(tmp);
}

